`include "./parameters.v"
`timescale 1ns/1ps
module sha3_wrap(
	input					clk,
	input 					rst_n,
	input 					enable,
	input 		[5:0]		model,

	input					ram_read_busy, //0为空闲，1为工作中
	input					ram_write_busy,
	output		wire 		read_req_sha3,
		
	input 		[95:0]		data_in,

	input		[3:0]		open_addr0,
	input		[3:0]		open_addr1,
	input		[3:0]		result_addr,

	output		 [`A_W-1:0]	addr0,
	output		 [`A_W-1:0]	addr1,
	output		 [`A_W-1:0]	addr2,	
	output		 [`A_W-1:0]	addr3,	
		
	output		[95:0]		data_out_a,
	output		[95:0]		data_out_b,

	// output 					read_req,
	output					valid_o,
	output					valid_o_rej,
	output					valid_o_bin,
	output					done,
	output					flag
	);
//************************************************************************************//
	wire [2:0]	width_type;
	wire [2:0]	read_width_type;
	reg			enable_buffer;
	reg			start;
	wire		read_req;

	always @(posedge clk or negedge rst_n)
	begin
		if(~rst_n) 
			enable_buffer <= 0;
		else 
			enable_buffer <= enable;
	end

	always @(posedge clk or negedge rst_n)
	begin
		if(~rst_n) 
			start <= 0;
		else if(enable && ~enable_buffer)
			start <= 1'b1;
		else
			start <= 1'b0;
	end

	addr_ctr	addr_ctr(
							.clk(clk),
							.rst_n(rst_n),
							.start(start),
							.model(model),
							.open_addr0(open_addr0),
							.open_addr1(open_addr1),
							.result_addr(result_addr),
							.read_req(read_req),
							.valid_o(valid_o),
							.valid_o_rej(valid_o_rej),
							.valid_o_bin(valid_o_bin),
							.addr0(addr0),
							.addr1(addr1),
							.addr2(addr2),
							.addr3(addr3),
							.width_type(width_type),
							.read_width_type(read_width_type),
							.flag(flag));
//************************************************************************************//
	
	wire valid_o_convert;
	// wire read_addr_change;
	wire [63:0]	data_out_convert;

	width_convert	width_convert(.clk       (clk),
								  .rst_n     (rst_n),
								  .enable    (enable),
								  .width_type(width_type),
								  .read_width_type(read_width_type),
								  .data_in   (data_in),
								  .read_req_sha3(read_req_sha3),
								  .read_req  (read_req),

								  .valid_o   (valid_o_convert),
								  .data_out  (data_out_convert));
//************************************************************************************//
	wire [95:0]	data_out_a_sha3,data_out_b_sha3;
	wire valid_o_sha3;
	wire done_rej;

	sha3 sha3(	.clk(clk),
				.rst_n(rst_n),
				.enable(enable),
				.model(model),
				.ram_read_busy(ram_read_busy),
				.ram_write_busy(ram_write_busy),
				.data_in(data_out_convert),
				.data_out_a(data_out_a_sha3),
				.data_out_b(data_out_b_sha3),
				.read_en(valid_o_convert),
				.read_req(read_req_sha3),
				// .read_addr_change(read_addr_change),
				.valid_o(valid_o_sha3),
				.done(done),
				.done_rej(done_rej));
//************************************************************************************//
	wire enable_rej;
	wire [47:0]	data_in_rej;
	wire [95:0]	data_out_rej;
	wire read_en_rej;
	// wire valid_o_rej;

	assign enable_rej = model[5:4] == `SHAKE128;
	assign data_in_rej = data_out_a_sha3[47:0];
	assign read_en_rej = valid_o_sha3;

	rej_sample	rej_sample(.clk     (clk),
						   .rst_n   (rst_n),
						   .enable  (enable_rej),
						   .data_in (data_in_rej),
						   .data_out(data_out_rej),
						   .read_en (read_en_rej),
						   .valid_o (valid_o_rej),
						   .done    (done_rej));
//************************************************************************************//
	wire enable_bin;
	wire model_bin;
	wire [95:0]	data_in_bin;
	wire [95:0]	data_out_a_bin,data_out_b_bin;
	wire read_en_bin;
	// wire valid_o_bin;

	assign enable_bin = model[5:4] == `SHAKE256 && model[3:0] != 4'b1110;
	assign model_bin = (model[3:0] == 4'b1000) || (model[3:0] == 4'b1001) || (model[3:0] == 4'b1010) || (model[3:0] == 4'b1011);
	assign data_in_bin = data_out_a_sha3;
	assign read_en_bin = valid_o_sha3;

	binomial	binomial(.clk     (clk),
						 .rst_n   (rst_n),
						 .enable  (enable_bin),
						 .model   (model_bin),
						 .data_in (data_in_bin),
						 .data_out_a(data_out_a_bin),
						 .data_out_b(data_out_b_bin),
						 .read_en (read_en_bin),
						 .valid_o (valid_o_bin));
//************************************************************************************//

	assign data_out_a = enable_rej ? data_out_rej :
						enable_bin ? data_out_a_bin :
									 data_out_a_sha3;

	assign data_out_b = enable_bin ? data_out_b_bin : data_out_b_sha3;

	assign valid_o = enable_rej ? valid_o_rej :
					 enable_bin ? valid_o_bin :
								  valid_o_sha3;



endmodule


module sha3_wrap_tb();
	
	reg					clk;
	reg 				rst_n;
	reg 				enable;
	reg 		[5:0]	model;
		
	reg 		[95:0]	data_in;

	reg		[3:0]		open_addr0;
	reg		[3:0]		open_addr1;
	reg		[3:0]		result_addr;

	wire		 [`A_W-1:0]	addr0;
	wire		 [`A_W-1:0]	addr1;
	wire		 [`A_W-1:0]	addr2;	
	wire		 [`A_W-1:0]	addr3;	
		
	wire		[95:0]		data_out_a;
	wire		[95:0]		data_out_b;

	// output 					read_req,
	wire					valid_o;
	wire					valid_o_rej;
	wire					valid_o_bin;
	wire					done;
	wire					flag;

	sha3_wrap sha3_wrap_tb(
	.clk			(clk),
	.rst_n			(rst_n),
	.enable			(enable),
	.model			(model),
	
	.ram_read_busy	(1'b0),
	.ram_write_busy	(1'b0),
	.read_req_sha3	(),

	.data_in		(data_in),

	.open_addr0		(open_addr0),
	.open_addr1		(open_addr1),
	.result_addr	(result_addr),

	.addr0			(addr0),
	.addr1			(addr1),
	.addr2			(addr2),	
	.addr3			(addr3),	
	
	.data_out_a		(data_out_a),
	.data_out_b		(data_out_b),

	.valid_o		(valid_o),
	.valid_o_rej	(valid_o_rej),
	.valid_o_bin	(valid_o_bin),
	.done			(done),
	.flag			(flag)
	);

	always #5 clk = ~clk;

	initial begin
		clk = 0;
		rst_n = 0;
		enable = 0;
		model = 6'b000000;
		data_in = 0;
		open_addr0 = 0;
		open_addr1 = 0;
		result_addr = 0;
		#20 
		rst_n = 1;
		#10
		enable = 1;
//		model = 6'b100000; //输入256bit XOF
		model = 6'b011000; //输入256bit PRF eta=3
		#50
		// data_in = 96'h0b0a09080706050403020100;
		// #10
		// data_in = 96'h17161514131211100f0e0d0c;
		// #10
		// data_in = 96'h000000001f1e1d1c1b1a1918;
		data_in = 96'h0000000017e0a7e5ea184cbe;
		#10
		data_in = 96'h000000002e2624de90a05fe3;
		#10
		data_in = 96'h00000000a8b3dfd7d11a9570;
		#10
		data_in = 96'h00000000f27918fb34116dc9;
	end


endmodule